package ALMySqlCommon.ALMySqlSafeOp;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import ALBasicCommon.ALBasicCommonFun;
import ALBasicCommon._ATALHandleFileLine;
import ALServerLog.ALServerLog;

/*******************
 * 数据库操作的文件信息保存和操作对象
 * 文件会分为两个部分保存
 * 1- 语句和内容保存文件
 * 2- 当前处理的序列号或动态信息存储文件
 * @author mj
 *
 */
public class ALMySqlSafeOpDBCacheFile 
{
	//是否初始化完成
	private boolean _m_bIsInited;
	
	//对应的数据库对象
	@SuppressWarnings("rawtypes")
	private _TALMySqlSafeOpDBObj _m_doDBObj;
	
	//后续不断插入需要操作的数据的写入句柄
	//在没有清空的情况下，句柄将保持在内存中。避免不断开关的IO性能问题
	private File _m_fCacheFile;
	private FileOutputStream _m_fosCacheFileOutputStream;
	private OutputStreamWriter _m_oswCacheInfoWriter;

	@SuppressWarnings("rawtypes")
	public ALMySqlSafeOpDBCacheFile(_TALMySqlSafeOpDBObj _dbObj)
	{
		_m_bIsInited = false;
		
		_m_doDBObj = _dbObj;
		_m_oswCacheInfoWriter = null;
	}
	
	/************
	 * 获取正在操作的信息存储文件路径
	 * @return
	 */
	public String getCacheFilePath() {return _m_doDBObj.getSavePath() + ".cache";}
	public String getDealingInfoFilePath() {return _m_doDBObj.getSavePath() + ".dealing";}
	
	/***********
	 * 
	 * @return
	 */
	public String makeBackupCacheFilePath() {return _m_doDBObj.getSavePath() + "_" + ALBasicCommonFun.getNowTime() + ".backup";}
	
	/*****************
	 * 初始化本文件管理器，带入的变量用于接收还未操作的数据库操作内容
	 * @param _recQueList
	 * @return
	 */
	public boolean init(List<ALMySqlSafeOpData> _recQueList)
	{
		if(_m_bIsInited)
		{
			ALServerLog.Error(_m_doDBObj.getSavePath() + " MySqlOpCacheFile already inited!");
			return false;
		}
		
		//初始化文件，并根据实际文件内容进行相关数据的读取
		String dealingFileInfo = ALBasicCommonFun.readFileTxt(getDealingInfoFilePath(), StandardCharsets.UTF_8);
		//处理信息获取当前处理到的数据位置
		long dealSerialie = 0;
		if(null != dealingFileInfo && !dealingFileInfo.isEmpty())
		{
			//获取对应的处理序列号
			dealSerialie = Long.parseLong(dealingFileInfo);
		}
		
		final long readCulSerialize = dealSerialie;
		//构造处理对象
		_ATALHandleFileLine<ALMySqlSafeOpData> lineDealer = new _ATALHandleFileLine<ALMySqlSafeOpData>() {
			/*********
			 * 处理单行内容的处理类函数，返回对应数据的结构体
			 * @param _lineStr
			 * @param _recList
			 */
			@Override
			public ALMySqlSafeOpData handle(String _lineStr)
			{
				//读取每行的数据
				ALMySqlSafeOpData data = new ALMySqlSafeOpData();
				//读取失败则报错并返回
				if(!data.parseFromString(_lineStr))
				{
					ALServerLog.Error("Read ALMySqlSafeOpData Data Error!: " + _lineStr);
					return null;
				}
				
				//判断数据的序列号是否有效，无效则返回空
				if(data.getSerialize() < readCulSerialize)
					return null;
				
				return data;
			}
		};
		//逐个读取当前实际处理文件的数据，返回的是需要处理的数据队列
		ArrayList<ALMySqlSafeOpData> needDealQueryList = ALBasicCommonFun.handleEachLinesFromFile(getCacheFilePath(), lineDealer);
		//将数据加入队列
		_recQueList.addAll(needDealQueryList);
		
		//重新构建写入当前缓存文件的写入句柄，确保后续新增错误可以正常写入
		_m_fCacheFile = new File(getCacheFilePath());
		if(null == _m_fCacheFile)
		{
			ALServerLog.Error("Can not create file Object for: " + getCacheFilePath());
			return false;
		}
		//判断父目录是否存在，如果不存在会引起创建错误
		File parentFile = _m_fCacheFile.getParentFile();
		if(null == parentFile)
		{
			ALServerLog.Error("Can not get CacheFile's Parent Folder! cache file path: " + getCacheFilePath());
			return false;
		}
		
        if (!parentFile.exists())
        {
         	if(!parentFile.mkdirs())
         	{
         		System.out.println("Can not create dir for file: " + getCacheFilePath());
         		return false;
         	}
        }
		
        //注意创建流的死后参数需要带入append为true，保证从后开始添加
		try {
	        //判断文件是否存在，不存在则创建新文件
	        if(!_m_fCacheFile.exists())
	        {
	        	if(!_m_fCacheFile.createNewFile())
	        	{
	         		System.out.println("Can not create File for file: " + getCacheFilePath());
	         		return false;
	        	}
	        }
	        
			_m_fosCacheFileOutputStream = new FileOutputStream(_m_fCacheFile, true);
			
			if(null == _m_fosCacheFileOutputStream)
			{
				ALServerLog.Error("Can not create file Stream for: " + getCacheFilePath());
				return false;
			}
			
			//创建写入的流对象
			_m_oswCacheInfoWriter = new OutputStreamWriter(_m_fosCacheFileOutputStream);
		} catch (FileNotFoundException e) {
     		System.out.println("Can not create File Stream for file: " + getCacheFilePath());
			e.printStackTrace();
			
			//返回失败
			return false;
		}catch (IOException e) {
     		System.out.println("Can not create File Stream(IO Exception) for file: " + getCacheFilePath());
			e.printStackTrace();
			
			//返回失败
			return false;
		}
		
		//设置初始化完成
		_m_bIsInited = true;
		
		return true;
	}
	
	/***********************
	 * 往文件末尾添加需要处理的数据
	 * 一般只在处理出现错误的时候才进行这个插入处理
	 * @param _data
	 */
	public void appendData(ALMySqlSafeOpData _data)
	{
		if(!_m_bIsInited)
		{
			ALServerLog.Error("Try Add MySql Safe OpData Fail!: " + _data.toString());
			return ;
		}
		
		//往文件末尾添加
		if(null == _m_oswCacheInfoWriter)
		{
			ALServerLog.Error("Try Write MySql Safe OpData Fail!: " + _data.toString());
			return ;
		}

		try {
			_m_oswCacheInfoWriter.write(_data.toString());
			_m_oswCacheInfoWriter.write("\n");
			_m_oswCacheInfoWriter.flush();
		} catch (IOException e) {
			ALServerLog.Error("Deal Write MySql Safe OpData Fail!: " + _data.toString());
			e.printStackTrace();

			return ;
		}
	}
	
	/*****************
	 * 设置当前处理的序列号
	 * @param _dealingSerialize
	 */
	public void setCurDealingSerialize(long _dealingSerialize)
	{
		ALBasicCommonFun.rewriteTxtToFile(getDealingInfoFilePath(), Long.toString(_dealingSerialize), StandardCharsets.UTF_8);
	}
	
	/***************
	 * 重置所有写入的信息以及内容
	 * 当所有待操作部分都操作完成的时候，会进行重置的操作
	 */
	public void resetAllFile(long _curDealingSerialize)
	{
		if(!_m_bIsInited)
		{
			ALServerLog.Error("Try Reset Op DB Cahe file when it is not inited!");
			return ;
		}

		try {
			if(null != _m_oswCacheInfoWriter)
				_m_oswCacheInfoWriter.close();
			_m_oswCacheInfoWriter = null;
			
			if(null != _m_fosCacheFileOutputStream)
				_m_fosCacheFileOutputStream.close();
			_m_fosCacheFileOutputStream = null;
			
			if(null == _m_fCacheFile)
			{
				_m_fCacheFile = new File(getCacheFilePath());
			}
			
			//进行文件重置删除处理
			if(null != _m_fCacheFile)
			{
				if(_m_fCacheFile.exists())
				{
					//重命名为另外文件，以备查询
					File renameFile = new File(makeBackupCacheFilePath());
					_m_fCacheFile.renameTo(renameFile);
				}
				
				//再创建新文件
				_m_fCacheFile.createNewFile();
			}
			
			//重置操作序列号
			setCurDealingSerialize(_curDealingSerialize);
	        
			//重新构建写入文件的句柄
			_m_fosCacheFileOutputStream = new FileOutputStream(_m_fCacheFile, true);
			
			if(null == _m_fosCacheFileOutputStream)
			{
				ALServerLog.Error("Can not create file Stream for: " + getCacheFilePath());
				return ;
			}
			
			//创建写入的流对象
			_m_oswCacheInfoWriter = new OutputStreamWriter(_m_fosCacheFileOutputStream);
		} catch (IOException e) {
			ALServerLog.Error("Create new Cache File Error: " + getCacheFilePath());
			e.printStackTrace();
		}
	}
}
